/*Author: Rony Lahav (ID:043489889)*/

import java.util.Collection;
import java.util.HashMap;
import java.util.SortedMap;

/**
 * This class handles parsing and creating messages
 */
public class MessageCenter {

	
	private static MessageCenter messageCenter = null;
	private static Logger logger=null;

	private String msg;
	private String peerID;
	private String peerIP;
	private String messageType;
	
	public static String newline = "\n";//System.getProperty("line.separator");
	
	/*Global parameters of the different variables and message types.
	 * used in this class and in other classes as well*/
	
	public final static String TYPE_AV = "AV";
	public final static String TYPE_IM = "IM";
	public final static String TYPE_FT = "FT";
	public final static String TYPE_GET = "GET";
	public final static String TYPE_SNLIST = "SNLIST";
	
	public final static String VAR_ID = "ID";
	public final static String VAR_MSG = "MSG";
	public final static String VAR_TCP = "TCP";
	public final static String VAR_FILE = "FILE";
	public final static String VAR_SIZE= "SIZE";
	public final static String VAR_SN= "SUPER-NODE";
	
	
	//Singleton CTOR
	private MessageCenter() {
		logger = Logger.getInstance();
		this.msg=null;
		this.peerID=null;
		this.peerIP=null;
		this.messageType=null;
	}
	
	//gets an in-message, checks for valid format and content and parses the parameters
	public HashMap<String,String> parseValidMessage(String message, String peerID, String peerIP) {
		if (message == null)
			return null;
		//update FROM data
		this.peerID=peerID;
		this.peerIP=peerIP;
		this.msg=message;
		
		String[] msgLines = message.split(newline);

		//if one of the checks throws an exception because of any unexpected reason we return an unknown error
		try {
			if (!final_project.isShutDown()){
				if (msgLines[0].endsWith(newline))
					msgLines[0]=msgLines[0].substring(0, msgLines[0].length() - newline.length());
				messageType = msgLines[0].trim().toUpperCase(); 
				if (messageType.compareTo(TYPE_AV) == 0)
				{
					return parseValidMessage_AV(msgLines);
				}
				if (messageType.compareTo(TYPE_FT) == 0)
				{
					return parseValidMessage_FT(msgLines);
				}
				if (messageType.compareTo(TYPE_GET) == 0)
				{
					return parseValidMessage_GET(msgLines);
				}
				if (messageType.compareTo(TYPE_IM) == 0)
				{
					return parseValidMessage_IM(msgLines);
				}
				if (messageType.compareTo(TYPE_SNLIST) == 0)
				{
					return parseValidMessage_SNLIST(msgLines);
				}
			}
		}
		catch(Exception e){
			return logger.printErrorMessage("ERROR - Parsing", messageType, this.peerID, this.peerIP);
		}
		
		return logger.printErrorMessage("ERROR - Parsing", messageType, this.peerID, this.peerIP);
	}
	
	//checks SNLIST message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_SNLIST(String[] msgLines) {
		if (!final_project.isShutDown()){
			//at least 1 SN besides header
			if (msgLines.length < 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			HashMap<String,String> params = new HashMap<String,String>();
			params.put("TYPE", messageType);
			//each line is an SN
			for (int i=1;i<msgLines.length;++i){
				String peerData = msgLines[i].split(": ")[1];
				String[] peerIPPort = peerData.split(",");
				DBTuple newPeer = new DBTuple();
				newPeer.setPeerIP(peerIPPort[0]);
				newPeer.setUdpPort(peerIPPort[1]);
				newPeer.setStatus("0");
				newPeer.setIsSN(true);
				newPeer.setLastConnectionTime(System.currentTimeMillis());
				newPeer.setConnectedThrough(final_project.getMyKey());	//i am updated as the link to the SN
				DataBase.getInstance().updateSN(newPeer);
			}
			return params;
		}
		return null;
	}
	
	//checks AV message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_AV(String[] msgLines) {
		String[] paramData=null;
		if (!final_project.isShutDown()){
			//1 parameter (ID) besides header
			if (msgLines.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			
			//parameter no.1 == ID
			if (!msgLines[1].startsWith(VAR_ID+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[1].endsWith(newline))
				msgLines[1]=msgLines[1].substring(0, msgLines[1].length() - newline.length());
			paramData=msgLines[1].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			HashMap<String,String> params = new HashMap<String,String>();
			params.put(VAR_ID, paramData[1]);
			String ID=paramData[1];
			params.put("TYPE", messageType);
			Logger.getInstance().printIncomingMsg(messageType, "", ID, peerIP);
			return params;
		}
		return null;
	}
	
	//checks FT message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_FT(String[] msgLines) {
		String[] paramData=null;
		if (!final_project.isShutDown()){
			//4 parameter (ID,TCP,FILE,SIZE) besides header
			if (msgLines.length != 5)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			
			//parameter no.1 == ID
			if (!msgLines[1].startsWith(VAR_ID+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[1].endsWith(newline))
				msgLines[1]=msgLines[1].substring(0, msgLines[1].length() - newline.length());
			paramData=msgLines[1].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			HashMap<String,String> params = new HashMap<String,String>();
			params.put(VAR_ID, paramData[1]);
			this.peerID=paramData[1];
			//parameter no.2 == TCP
			if (!msgLines[2].startsWith(VAR_TCP+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[2].endsWith(newline))
				msgLines[2]=msgLines[2].substring(0, msgLines[2].length() - newline.length());
			paramData=msgLines[2].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			params.put(VAR_TCP, paramData[1]);
			
			//parameter no.3 == FILE
			if (!msgLines[3].startsWith(VAR_FILE+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[3].endsWith(newline))
				msgLines[3]=msgLines[3].substring(0, msgLines[3].length() - newline.length());
			paramData=msgLines[3].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			params.put(VAR_FILE, paramData[1]);
			String fileName = paramData[1];
			//parameter no.4 == SIZE
			if (!msgLines[4].startsWith(VAR_SIZE+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[4].endsWith(newline))
				msgLines[4]=msgLines[4].substring(0, msgLines[4].length() - newline.length());
			paramData=msgLines[4].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			params.put(VAR_SIZE, paramData[1]);
			
			params.put("TYPE", messageType);
			Logger.getInstance().printIncomingMsg(messageType, fileName, peerID, peerIP);
			return params;
		}
		return null;
	}
	
	//checks GET message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_GET(String[] msgLines) {
		String[] paramData=null;
		if (!final_project.isShutDown()){
			//2 parameter (ID,FILE) besides header
			if (msgLines.length != 3)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			
			//parameter no.1 == ID
			if (!msgLines[1].startsWith(VAR_ID+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[1].endsWith(newline))
				msgLines[1]=msgLines[1].substring(0, msgLines[1].length() - newline.length());
			paramData=msgLines[1].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			HashMap<String,String> params = new HashMap<String,String>();
			params.put(VAR_ID, paramData[1]);
			this.peerID=paramData[1];
			//parameter no.2 == FILE
			if (!msgLines[2].startsWith(VAR_FILE+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[2].endsWith(newline))
				msgLines[2]=msgLines[1].substring(0, msgLines[2].length() - newline.length());
			paramData=msgLines[2].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			params.put(VAR_FILE, paramData[1]);
			String fileName = paramData[1];
			params.put("TYPE", messageType);
			Logger.getInstance().printIncomingMsg(messageType, fileName, peerID, peerIP);
			return params;
		}
		return null;
	}
	
	//checks IM message validity and parses the parameters
	private HashMap<String,String> parseValidMessage_IM(String[] msgLines) {
		String[] paramData=null;
		if (!final_project.isShutDown()){
			//2 parameter (ID,MSG) besides header
			if (msgLines.length != 3)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			
			//parameter no.1 == ID
			if (!msgLines[1].startsWith(VAR_ID+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[1].endsWith(newline))
				msgLines[1]=msgLines[1].substring(0, msgLines[1].length() - newline.length());
			paramData=msgLines[1].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			HashMap<String,String> params = new HashMap<String,String>();
			params.put(VAR_ID, paramData[1]);
			this.peerID=paramData[1];
			//parameter no.2 == MSG
			if (!msgLines[2].startsWith(VAR_MSG+": "))
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			if (msgLines[2].endsWith(newline))
				msgLines[2]=msgLines[1].substring(0, msgLines[2].length() - newline.length());
			paramData=msgLines[2].split(": ");
			//var+value
			if (paramData.length != 2)
				return logger.printErrorMessage("ERROR - Wrong Format", messageType, this.peerID, this.peerIP);
			params.put(VAR_MSG, paramData[1]);
			String msg = paramData[1];
			params.put("TYPE", messageType);
			Logger.getInstance().printIncomingMsg(messageType, msg, peerID, peerIP);
			return params;
		}
		return null;
	}	
	
	//singleton
	public static MessageCenter getInstance() {
		if (messageCenter == null) {
			messageCenter = new MessageCenter();
		}
		
		return messageCenter;
	}
	
	//creates an AV message
	public String createMessage_AV(){
		if (!final_project.isShutDown())
			return TYPE_AV + newline
					+ VAR_ID + ": " + final_project.getMyPeerID() + newline;
		return "";
	}
	
	//creates an FT message
	public String createMessage_FT(String TCP, String File, String Size){
		if (!final_project.isShutDown())
			return TYPE_FT + newline
					+ VAR_ID + ": " + final_project.getMyPeerID() + newline
					+ VAR_TCP + ": " + TCP + newline
					+ VAR_FILE + ": " + File + newline
					+ VAR_SIZE + ": " + Size + newline;
		return "";
	}
	
	//creates an FT message with custom ID
	public String createMessage_FT(String TCP, String File, String Size, String senderID){
		if (!final_project.isShutDown())
			return TYPE_FT + newline
					+ VAR_ID + ": " + senderID + newline
					+ VAR_TCP + ": " + TCP + newline
					+ VAR_FILE + ": " + File + newline
					+ VAR_SIZE + ": " + Size + newline;
		return "";
	}
	
	//creates an GET message
	public String createMessage_GET(String File){
		if (!final_project.isShutDown())
			return TYPE_GET + newline
					+ VAR_ID + ": " + final_project.getMyPeerID() + newline
					+ VAR_FILE + ": " + File + newline;
		return "";
	}
	
	//creates an IM message
	public String createMessage_IM(String MSG){
		if (!final_project.isShutDown())
			return TYPE_IM + newline
					+ VAR_ID + ": " + final_project.getMyPeerID() + newline
					+ VAR_MSG + ": " + MSG + newline;
		return "";
	}
	
	//creates an SNLIST message for peers (without connected through
	public String createMessage_SNLIST(){
		if (!final_project.isShutDown()){
			String msg = TYPE_SNLIST + newline;
			SortedMap<String, DBTuple> snList = DataBase.getInstance().getSnList();
			if (snList!=null){
				Collection<DBTuple> c = snList.values();
				for (DBTuple sn : c) {
					msg +=  VAR_SN + ": " + sn.getPeerIP() + "," + sn.getUdpPort() + newline;
				}
			}
			return msg;
		}
		return "";
	}

	//creates an SNLIST message for SNs
	public String createMessage_SNLIST_ForSNs(boolean isReply){
		if (!final_project.isShutDown()){
			String msg = TYPE_SNLIST + newline;
			if (isReply)
				msg = "$" + msg;
			SortedMap<String, DBTuple> snList = DataBase.getInstance().getSnList();
			if (snList!=null){
				Collection<DBTuple> c = snList.values();
				for (DBTuple sn : c) {
					String connectedThrough = sn.getConnectedThrough();
					if (!connectedThrough.equals("0"))
						connectedThrough = final_project.getMyKey();
					msg +=  VAR_SN + ": " + sn.getPeerIP() + "," + sn.getUdpPort() + "," + connectedThrough + newline;
				}
			}
			return msg;
		}
		return "";
	}
}
